package logic

import (
	"gameserver/server/DBserver/internal"

	"gitee.com/night-tc/gobige/dbmanager"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
	"gorm.io/gorm/callbacks"
)

type IMessage interface {
	Handler(lth *Player) //处理消息
	IsPreSave() bool     //是否需要先保存一下
}
type DBMessage struct {
}

func (msg *DBMessage) IsPreSave() bool {
	return false
}

type SaveMessage struct {
}

func (msg *SaveMessage) IsPreSave() bool {
	return true
}
func (msg *SaveMessage) Handler(lth *Player) {
}

type DBUpUserDatasReq struct {
	DBMessage
	*protomsg.DBUpUserDatasReq
}

// 更新玩家数据库字段
func (msg *DBUpUserDatasReq) Handler(lth *Player) {
	defer func() {
		if err := recover(); err != nil {
			logger.Error("DBUpUserDatasReq Handler Catch Ignored Unknown Panic: ", err)
		}
	}()
	for _, v := range msg.DBList {
		/*
			下面的逻辑如果是全量更新，那不需要改
			但如果是差量跟更就不行需要使用新的逻辑
		*/
		var md dbmanager.IDataBaseModel
		var ok bool
		var data *Data
		if v.IsPartial {
			msgdata := new(protomsg.TableData)
			if err := msgdata.Unmarshal(v.MsgContent); err != nil {
				continue
			}
			if data, ok = lth.dirtyList[v.KeyName]; ok {
				md = data.Model
			} else if md, ok = lth.cacheList[v.KeyName]; ok {
			} else {
				md = dbmanager.GetDBSingleton().GetModelByKeyName(v.KeyName)
				if md.GetIsPartial() {
					//如果是差量修改的话，需要从DB上加载先
					if _, ok := md.(dbmanager.IPlayerSubBase); ok {
						_, id := dbmanager.GetKeyNameResolve(v.KeyName)
						//如果是一表多行的数据
						lth.conndb.Model(md).Unscoped().Where("id = ?", id).First(md)

					} else {
						lth.conndb.Model(md).Unscoped().Where("pid = ?", lth.playerID).First(md)
					}
					lth.cacheList[v.KeyName] = md
				}
			}
			if data == nil {
				data = &Data{
					Model:     md,
					IsPartial: true,
					Columns:   []string{},
				}
				if md.GetPID() == 0 {
					/*
						表示DB上没有这个数据
						目标只有Player数据会走这个逻辑，因为需要走单字段的打包方式
						如果不是那就会有问题了，需要提示
					*/
					data.IsPartial = false
					data.debug = true
				}
			}
			lth.dirtyList[v.KeyName] = data
			SetDataModel(data, msgdata)
			// dbmanager.SetDataModel(md, msgdata)
		} else {
			md := dbmanager.GetDBSingleton().GetModelByKeyName(v.KeyName)
			md.Unmarshal(v.MsgContent)
			data = &Data{
				Model:     md,
				IsPartial: false,
				Columns:   []string{},
			}
			lth.dirtyList[v.KeyName] = data
			lth.cacheList[v.KeyName] = md

		}
	}
	for _, v := range msg.DelList {

		if v.IsPartial {
			msgdata := new(protomsg.TableData)
			if err := msgdata.Unmarshal(v.MsgContent); err != nil {
				continue
			}
			if md, ok := lth.dellist[v.KeyName]; ok {
				dbmanager.SetDataModel(md, msgdata)
				delete(lth.cacheList, md.KeyName())
				lth.dellist[md.KeyName()] = md
			} else if md, ok = lth.cacheList[v.KeyName]; ok {
				// md = dbmanager.GetDBSingleton().GetModelByKeyName(v.KeyName)
				dbmanager.SetDataModel(md, msgdata)
				delete(lth.cacheList, md.KeyName())
				lth.dellist[v.KeyName] = md
			} else {
				md = dbmanager.GetDBSingleton().GetModelByKeyName(v.KeyName)
				if md.GetIsPartial() {
					//如果是差量修改的话，需要从DB上加载先
					if _, ok := md.(dbmanager.IPlayerSubBase); ok {
						_, id := dbmanager.GetKeyNameResolve(v.KeyName)
						//如果是一表多行的数据
						if lth.conndb.Model(md).Unscoped().Where("id = ?", id).First(md).Error == nil {
							dbmanager.SetDataModel(md, msgdata)
						}
					} else {
						if lth.conndb.Model(md).Unscoped().Where("pid = ?", lth.playerID).First(md).Error == nil {
							dbmanager.SetDataModel(md, msgdata)
						}
					}
					// lth.cacheList[v.KeyName] = md
				}
				md.SetDelete()
				lth.dellist[v.KeyName] = md
			}
		} else {
			md := dbmanager.GetDBSingleton().GetModelByKeyName(v.KeyName)
			md.Unmarshal(v.MsgContent)
			delete(lth.dellist, md.KeyName())
			lth.dellist[md.KeyName()] = md
		}

	}
}

type DBGetPlayerInfoReq struct {
	DBMessage
	*protomsg.DBGetPlayerInfoReq
}

// 获取玩家数据库字段
func (msg *DBGetPlayerInfoReq) Handler(lth *Player) {
	count := 20
	ack := new(protomsg.DBGetPlayerInfoAck)
	ack.BackKey = msg.BackKey
	ack.DBList = make([]*protomsg.DBDataModel, 0, count)
	for i := range msg.Tables {
		md := dbmanager.GetDBSingleton().GetTypeByTname(msg.Tables[i])
		rows, err := lth.conndb.Model(md).Where("pid = ?", lth.playerID).Rows()
		if err != nil {
			logger.Error("DBGetPlayerInfoReq:", err)
			continue
		}
		func() {
			defer rows.Close()
			for rows.Next() {
				md := dbmanager.GetDBSingleton().GetTypeByTname(msg.Tables[i])
				lth.conndb.ScanRows(rows, md)
				if tmp, ok := md.(callbacks.AfterFindInterface); ok {
					if err := tmp.AfterFind(&lth.conndb.DB); err != nil {
						logger.Error("DBGetPlayerInfoReq.AfterFindInterface:", err)
						continue
					}
				}
				buf, _ := md.Marshal()
				ack.DBList = append(ack.DBList, &protomsg.DBDataModel{
					KeyName:    md.KeyName(),
					MsgContent: buf,
				})
				//如果这个数据是可以部分字段更新，就需要缓存数据
				if md.GetIsPartial() {
					lth.cacheList[md.KeyName()] = md
				}
				if len(ack.DBList) >= count {
					internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, ack)
					// dbmanager.GetDBSingleton().PostOther(msg.PID, msg.SpaceID, msg.SrvID, ack)
					// proc.player.Post(uint8(msg.SrvType), ack)
					ack = new(protomsg.DBGetPlayerInfoAck)
					ack.BackKey = msg.BackKey
					ack.DBList = make([]*protomsg.DBDataModel, 0, count)
				}
			}
		}()

		if len(ack.DBList) >= count {
			internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, ack)
			// proc.player.Post(uint8(msg.SrvType), ack)
			ack = new(protomsg.DBGetPlayerInfoAck)
			ack.BackKey = msg.BackKey
			ack.DBList = make([]*protomsg.DBDataModel, 0, count)
		}
	}
	if len(ack.DBList) > 0 {
		internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, ack)
		// dbmanager.GetDBSingleton().PostOther(msg.PID, msg.SpaceID, msg.SrvID, ack)
		// proc.player.Post(uint8(msg.SrvType), ack)
	}
	endack := new(protomsg.DBGetPlayerInfoEndAck)
	endack.BackKey = msg.BackKey
	internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, endack)
	// proc.player.Post(uint8(msg.SrvType), endack)
}

func (msg *DBGetPlayerInfoReq) IsPreSave() bool {
	return true
}

type DBGetPlayerTableReq struct {
	DBMessage
	*protomsg.DBGetPlayerTableReq
}

// 获取玩家 数据库列表
func (msg *DBGetPlayerTableReq) Handler(lth *Player) {
	count := 20
	ack := new(protomsg.DBGetPlayerInfoAck)
	ack.BackKey = msg.BackKey
	ack.DBList = make([]*protomsg.DBDataModel, 0, count)
	md := dbmanager.GetDBSingleton().GetTypeByTname(msg.Table)
	//返回的数据，按ID倒序，就是按最新时间获取，获取指定行数
	rows, err := lth.conndb.Model(md).Where("pid = ?", lth.playerID).Order("created_at DESC").Limit(int(msg.Limit)).Rows()
	ack.BackKey = msg.BackKey
	ack.DBList = make([]*protomsg.DBDataModel, 0, count)
	if err != nil {
		logger.Error("DBGetPlayerTableReq:", err)
		return
	}
	func() {
		defer rows.Close()
		for rows.Next() {
			md := dbmanager.GetDBSingleton().GetTypeByTname(msg.Table)
			lth.conndb.ScanRows(rows, md)
			if tmp, ok := md.(callbacks.AfterFindInterface); ok {
				if err := tmp.AfterFind(&lth.conndb.DB); err != nil {
					logger.Error("DBGetPlayerTableReq.AfterFindInterface:", err)
					continue
				}
			}
			buf, _ := md.Marshal()
			ack.DBList = append(ack.DBList, &protomsg.DBDataModel{
				KeyName:    md.KeyName(),
				MsgContent: buf,
			})
			//如果这个数据是可以部分字段更新，就需要缓存数据
			if md.GetIsPartial() {
				lth.cacheList[md.KeyName()] = md
			}
			if len(ack.DBList) >= count {
				internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, ack)
				// dbmanager.GetDBSingleton().PostOther(msg.PID, msg.SpaceID, msg.SrvID, ack)
				// proc.player.Post(uint8(msg.SrvType), ack)
				ack = new(protomsg.DBGetPlayerInfoAck)
				ack.BackKey = msg.BackKey
				ack.DBList = make([]*protomsg.DBDataModel, 0, count)
			}
		}
	}()

	if len(ack.DBList) > 0 {
		internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, ack)
		// proc.player.Post(uint8(msg.SrvType), ack)
	}
	endack := new(protomsg.DBGetPlayerInfoEndAck)
	endack.BackKey = msg.BackKey
	internal.GetSrvinst().PostMsgToEntity(msg.SrvID, msg.SpaceID, msg.EntityID, endack)
	// proc.player.Post(uint8(msg.SrvType), endack)
}

func (msg *DBGetPlayerTableReq) IsPreSave() bool {
	return true
}
